package com.gqz.common.core.utils.query;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.gqz.common.core.constant.QueryCondition;
import com.gqz.common.core.domain.RequestForm;
import com.gqz.common.core.enums.QueryRuleEnum;
import lombok.SneakyThrows;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;


public class QueryMapperUtil {
    public static final String BEGIN = "_begin";
    public static final String END = "_end";
    private static final String STAR = "*";
    private static final String COMMA = ",";
    private static final String NOT_EQUAL = "!";
    /**
     * 页面带有规则值查询，空格作为分隔符
     */
    private static final String QUERY_SEPARATE_KEYWORD = " ";
    /**
     * 排序列
     */
    private static final String ORDER_COLUMN = "column";
    /**
     * 排序方式
     */
    private static final String ORDER_TYPE = "order";
    private static final String ORDER_TYPE_ASC = "ASC";
    /**
     * 字典翻译文本后缀
     */
    public static final String DICT_TEXT_SUFFIX = "_dictText";
    /**
     * 高级查询前端传来的参数名
     */
    private static final String SUPER_QUERY_PARAMS = "superQueryParams";

    public static <T> MPJLambdaWrapper<T> initQueryWrapper(T searchObj, RequestForm requestForm) {
        Map<String, Object> parameterMap = requestForm.getParamsMap();
        //设置排序的字段
        if (ObjectUtils.isNotEmpty(requestForm.getColumn())) {
            parameterMap.put(ORDER_COLUMN, requestForm.getColumn());
        }
        //设置排序的类型 asc  desc
        if (ObjectUtils.isNotEmpty(requestForm.getOrder())) {
            parameterMap.put(ORDER_TYPE, requestForm.getOrder());
        }

        MPJLambdaWrapper<T> queryWrapper = new MPJLambdaWrapper<>();
        installMPJLambdaWrapper(queryWrapper, searchObj, parameterMap);
        return queryWrapper;
    }

    public static <T> MPJLambdaWrapper<T> initQueryWrapperParam(T searchObj, RequestForm requestForm, MPJLambdaWrapper<T> queryWrapper) {
        Map<String, Object> parameterMap = requestForm.getParamsMap();
        //设置排序的字段
        if (ObjectUtils.isNotEmpty(requestForm.getColumn())) {
            parameterMap.put(ORDER_COLUMN, requestForm.getColumn());
        }
        //设置排序的类型 asc  desc
        if (ObjectUtils.isNotEmpty(requestForm.getOrder())) {
            parameterMap.put(ORDER_TYPE, requestForm.getOrder());
        }

        installMPJLambdaWrapper(queryWrapper, searchObj, parameterMap);
        return queryWrapper;
    }

    /**
     * @param searchObj
     * @param parameterMap
     * @Author: 周铁斌
     * @Date: 2022/10/17 10:07
     * @Description: 动态生成 MPJLambdaWrapper
     * @return: com.github.yulichang.wrapper.MPJLambdaWrapper<T>
     */
    public static <T> MPJLambdaWrapper<T> initQueryWrapper(T searchObj, Map<String, Object> parameterMap) {
        MPJLambdaWrapper<T> queryWrapper = new MPJLambdaWrapper<>();
        installMPJLambdaWrapper(queryWrapper, searchObj, parameterMap);
        return queryWrapper;
    }

    /**
     * @param queryWrapper
     * @param searchObj
     * @param parameterMap
     * @Author: 周铁斌
     * @Date: 2022/10/17 10:07
     * @Description: 动态生成 MPJLambdaWrapper
     * @return: void
     */
    public static void installMPJLambdaWrapper(MPJLambdaWrapper<?> queryWrapper, Object searchObj, Map<String, Object> parameterMap) {
        //区间条件组装 模糊查询 高级查询组装 简单排序 权限查询
        PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(searchObj);
        String name, type;

        for (int i = 0, length = origDescriptors.length; i < length; i++) {
            name = StringUtils.uncapitalize(origDescriptors[i].getName());
//            name = origDescriptors[i].getName();
            type = origDescriptors[i].getPropertyType().toString();

            try {
                if (judgedIsUselessField(name) || !PropertyUtils.isReadable(searchObj, name)) {
                    continue;
                }

                // 添加 判断是否有区间值
                String endValue = null, beginValue = null;
                if (parameterMap != null && parameterMap.containsKey(name + BEGIN)) {
                    beginValue = parameterMap.get(name + BEGIN).toString().trim();
                    addQueryByRule(searchObj, queryWrapper, name, type, beginValue, QueryRuleEnum.GE);

                }
                if (parameterMap != null && parameterMap.containsKey(name + END)) {
                    endValue = parameterMap.get(name + END).toString().trim();
                    addQueryByRule(searchObj, queryWrapper, name, type, endValue, QueryRuleEnum.LE);
                }

                //判断单值  参数带不同标识字符串 走不同的查询
                //这种前后带逗号的支持分割后模糊查询需要否 使多选字段的查询生效
                Object value = PropertyUtils.getSimpleProperty(searchObj, name);
                if (ObjectUtils.isNotEmpty(value) && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) {
                    // 过滤空值
                    String multiLikeval = value.toString().replace(",,", COMMA);
                    String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA);
                    if (vals.length > 1) {
                        final String finalName = name;
                        queryWrapper.and((j) -> {
                            j = j.like(getColumn(searchObj, finalName), vals[0]);
                            for (int k = 1; k < vals.length; k++) {
                                j = j.or().like(getColumn(searchObj, finalName), vals[k]);
                            }
                        });
                    } else {
                        final String finalName = name;
                        queryWrapper.and(j -> j.like(getColumn(searchObj, finalName), vals[0]));
                    }
                } else {
                    //根据参数值带什么关键字符串判断走什么类型的查询
                    QueryRuleEnum rule = convert2Rule(value);
                    value = replaceValue(rule, value);
                    addEasyQuery(searchObj, queryWrapper, name, rule, value);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 排序逻辑 处理
        doMultiFieldsOrder(searchObj, queryWrapper, parameterMap);
        //高级查询
        doSuperQuery(searchObj, queryWrapper, parameterMap);
    }

    /**
     * 高级查询
     *
     * @param queryWrapper
     * @param parameterMap
     */

    public static void doSuperQuery(Object searchObj, MPJLambdaWrapper<?> queryWrapper, Map<String, Object> parameterMap) {
        if (CollectionUtils.isNotEmpty(parameterMap) && parameterMap.containsKey(SUPER_QUERY_PARAMS)) {
            String superQueryParams = parameterMap.get(SUPER_QUERY_PARAMS).toString();
            // 解码
            try {
                superQueryParams = URLDecoder.decode(superQueryParams, "UTF-8");
            } catch (UnsupportedEncodingException e) {
            }
            List<QueryCondition> conditions = JSON.parseArray(superQueryParams, QueryCondition.class);
            for (QueryCondition rule : conditions) {
                if ((!StringUtils.isEmpty(rule.getField())) && (!StringUtils.isEmpty(rule.getRule())) && (!StringUtils.isEmpty(rule.getVal()))) {
                    addEasyQuery(searchObj, queryWrapper, rule.getField(), QueryRuleEnum.getByValue(rule.getRule()), rule.getVal());
                }
            }
        }
    }

    /**
     * @param searchObj
     * @param queryWrapper
     * @param parameterMap
     * @Author: 周铁斌
     * @Date: 2022/10/17 09:22
     * @Description: 多字段排序
     * @return: void
     */
    public static void doMultiFieldsOrder(Object searchObj, MPJLambdaWrapper<?> queryWrapper, Map<String, Object> parameterMap) {
        String column = null, order = null;
        if (CollectionUtils.isNotEmpty(parameterMap) && parameterMap.containsKey(ORDER_COLUMN)) {
            column = parameterMap.get(ORDER_COLUMN).toString();
        }
        if (CollectionUtils.isNotEmpty(parameterMap) && parameterMap.containsKey(ORDER_TYPE)) {
            order = parameterMap.get(ORDER_TYPE).toString();
        }
        if ((!StringUtils.isEmpty(column)) && (!StringUtils.isEmpty(order))) {
            //字典字段，去掉字典翻译文本后缀
            if (column.endsWith(DICT_TEXT_SUFFIX)) {
                column = column.substring(0, column.lastIndexOf(DICT_TEXT_SUFFIX));
            }
            //SQL注入check
            SqlInjectionUtil.filterContent(column);
            if (order.toUpperCase().indexOf(ORDER_TYPE_ASC) >= 0) {
                queryWrapper.orderByAsc(getColumn(searchObj, column));
            } else {
                queryWrapper.orderByDesc(getColumn(searchObj, column));
            }
        }
    }

    /*
     * 替换掉关键字字符
     *
     * @param rule
     * @param value
     * @return
     */
    private static Object replaceValue(QueryRuleEnum rule, Object value) {
        if (rule == null) {
            return null;
        }
        if (!(value instanceof String)) {
            return value;
        }
        String val = (value + "").toString().trim();
        if (rule == QueryRuleEnum.LIKE) {
            value = val.substring(1, val.length() - 1);
        } else if (rule == QueryRuleEnum.LEFT_LIKE || rule == QueryRuleEnum.NE) {
            value = val.substring(1);
        } else if (rule == QueryRuleEnum.RIGHT_LIKE) {
            value = val.substring(0, val.length() - 1);
        } else if (rule == QueryRuleEnum.IN) {
            value = val.split(",");
        } else {
            //update-begin--Author:  Date:20190724 for：initQueryWrapper组装sql查询条件错误 #284-------------------
            if (val.startsWith(rule.getValue())) {
                // 此处逻辑应该注释掉-> 如果查询内容中带有查询匹配规则符号，就会被截取的（比如：>=您好）
                value = val.replaceFirst(rule.getValue(), "");
            } else if (val.startsWith(rule.getCondition() + QUERY_SEPARATE_KEYWORD)) {
                value = val.replaceFirst(rule.getCondition() + QUERY_SEPARATE_KEYWORD, "").trim();
            }
            //update-end--Author:  Date:20190724 for：initQueryWrapper组装sql查询条件错误 #284-------------------
        }
        return value;
    }

    /**
     * 根据所传的值 转化成对应的比较方式
     * 支持><= like in !
     *
     * @param value
     * @return
     */
    private static QueryRuleEnum convert2Rule(Object value) {
        if (ObjectUtils.isEmpty(value)) {
            return null;
        }
        String val = (value + "").toString().trim();
        if (StringUtils.isEmpty(val)) {
            return null;
        }
        QueryRuleEnum rule = null;
        // 此处规则，只适用于 le lt ge gt
        // step 2 .>= =<
        if (rule == null && val.length() >= 3) {
            if (QUERY_SEPARATE_KEYWORD.equals(val.substring(2, 3))) {
                rule = QueryRuleEnum.getByValue(val.substring(0, 2));
            }
        }
        // step 1 .> <
        if (rule == null && val.length() >= 2) {
            if (QUERY_SEPARATE_KEYWORD.equals(val.substring(1, 2))) {
                rule = QueryRuleEnum.getByValue(val.substring(0, 1));
            }
        }

        // step 3 like
        if (rule == null && val.contains(STAR)) {
            if (val.startsWith(STAR) && val.endsWith(STAR)) {
                rule = QueryRuleEnum.LIKE;
            } else if (val.startsWith(STAR)) {
                rule = QueryRuleEnum.LEFT_LIKE;
            } else if (val.endsWith(STAR)) {
                rule = QueryRuleEnum.RIGHT_LIKE;
            }
        }

        // step 4 in
        if (rule == null && val.contains(COMMA)) {
            // 查询这里应该有个bug  如果一字段本身就是多选 此时用in查询 未必能查询出来
            rule = QueryRuleEnum.IN;
        }

        // step 5 !=
        if (rule == null && val.startsWith(NOT_EQUAL)) {
            rule = QueryRuleEnum.NE;
        }
        return rule != null ? rule : QueryRuleEnum.EQ;
    }

    private static void addQueryByRule(Object searchObj, MPJLambdaWrapper<?> queryWrapper, String name, String type, String value, QueryRuleEnum rule) throws ParseException {
        if (!(StringUtils.isEmpty(value))) {
            Object temp;
            switch (type) {
                case "class java.lang.Integer":
                    temp = Integer.parseInt(value);
                    break;
                case "class java.math.BigDecimal":
                    temp = new BigDecimal(value);
                    break;
                case "class java.lang.Short":
                    temp = Short.parseShort(value);
                    break;
                case "class java.lang.Long":
                    temp = Long.parseLong(value);
                    break;
                case "class java.lang.Float":
                    temp = Float.parseFloat(value);
                    break;
                case "class java.lang.Double":
                    temp = Double.parseDouble(value);
                    break;
                case "class java.util.Date":
                    temp = getDateQueryByRule(value, rule);
                    break;
                default:
                    temp = value;
                    break;
            }
            addEasyQuery(searchObj, queryWrapper, name, rule, temp);
        }
    }

    @SneakyThrows
    private static SFunction getColumn(Object searchObj, String column) {
        Class[] argsClass = {String.class};
        Object[] args = {column};
        Class ownerClass = searchObj.getClass();
        Method method = ownerClass.getMethod("genColumnSFunction", argsClass);
        return (SFunction) method.invoke(searchObj, args);
    }

    private static void addEasyQuery(Object searchObj, MPJLambdaWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
        if (value == null || rule == null || ObjectUtils.isEmpty(value)) {
            return;
        }
        switch (rule) {
            case GT:
                queryWrapper.gt(getColumn(searchObj, name), value);
                break;
            case GE:
                queryWrapper.ge(getColumn(searchObj, name), value);
                break;
            case LT:
                queryWrapper.lt(getColumn(searchObj, name), value);
                break;
            case LE:
                queryWrapper.le(getColumn(searchObj, name), value);
                break;
            case EQ:
                queryWrapper.eq(getColumn(searchObj, name), value);
                break;
            case NE:
                queryWrapper.ne(getColumn(searchObj, name), value);
                break;
            case IN:
                if (value instanceof String) {
                    queryWrapper.in(getColumn(searchObj, name), (Object[]) value.toString().split(","));
                } else if (value instanceof String[]) {
                    queryWrapper.in(getColumn(searchObj, name), (Object[]) value);
                } else {
                    queryWrapper.in(getColumn(searchObj, name), value);
                }
                break;
            case LIKE:
                queryWrapper.like(getColumn(searchObj, name), value);
                break;
            case LEFT_LIKE:
                queryWrapper.likeLeft(getColumn(searchObj, name), value);
                break;
            case RIGHT_LIKE:
                queryWrapper.likeRight(getColumn(searchObj, name), value);
                break;
            default:
                break;
        }

    }

    /**
     * 获取日期类型的值
     *
     * @param value
     * @param rule
     * @return
     * @throws ParseException
     */
    private static Date getDateQueryByRule(String value, QueryRuleEnum rule) throws ParseException {
        Date date = null;
        if (value.length() == 10) {
            if (rule == QueryRuleEnum.GE) {
                //比较大于
                date = getTime().parse(value + " 00:00:00");
            } else if (rule == QueryRuleEnum.LE) {
                //比较小于
                date = getTime().parse(value + " 23:59:59");
            }
        }
        if (date == null) {
            date = getTime().parse(value);
        }
        return date;
    }

    /**
     * 时间格式化
     */

    private static final ThreadLocal<SimpleDateFormat> local = new ThreadLocal<SimpleDateFormat>();

    private static SimpleDateFormat getTime() {
        SimpleDateFormat time = local.get();
        if (time == null) {
            time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            local.set(time);
        }
        return time;
    }

    private static boolean judgedIsUselessField(String name) {
        return "class".equals(name) || "ids".equals(name)
                || "page".equals(name) || "rows".equals(name)
                || "sort".equals(name) || "order".equals(name);
    }
}
